package org.miniframework.aop;

import org.miniframework.aop.annotation.Aspect;
import org.miniframework.aop.annotation.Order;
import org.miniframework.aop.aspect.AspectInfo;
import org.miniframework.aop.aspect.AspectInfo1;
import org.miniframework.aop.aspect.DefaultAspect;
import org.miniframework.core.BeanContainer;
import org.miniframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * AOP 2.0(支持表达式)
 */
public class AspectWeaver {

    private BeanContainer beanContainer;

    public AspectWeaver(){
        this.beanContainer = BeanContainer.getInstance();
    }

    public void doAop(){
        // 1.获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        if (ValidationUtil.isEmpty(aspectSet)){return;}
        // 2.拼装AspectInfoList
        List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
        // 3.遍历容器里的类
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> targetClass : classSet){
            // 排除 Aspect 自身
            if (targetClass.isAnnotationPresent(Aspect.class)){
                continue;
            }
            // 4.粗筛符合条件的Aspect
            List<AspectInfo> roughMatchdAspectList = collectRoughMatchedAspectListForSpecificClass(targetClass,aspectInfoList);
            // 5.尝试进行Aspect的拦截 (对目标对象进行织入切面,产生代理对象替换容器中原始对象)
            wrapIfNecessary(roughMatchdAspectList,targetClass);
        }
    }

    // 5.为目标对象尝试进行产生代理对象并替换目标对象
    private void wrapIfNecessary(List<AspectInfo> roughMatchdAspectList, Class<?> targetClass) {
        if (ValidationUtil.isEmpty(roughMatchdAspectList)){
            return;
        }
        Object proxyBean = ProxyCreator.createProxy(targetClass, new AspectListExecutor(targetClass, roughMatchdAspectList));
        beanContainer.addBean(targetClass,proxyBean);
    }

    // 4.粗筛符合条件的Aspect aspectInfoList 中是否有 该目标targetClass 织入切面对象,如果有放入粗筛集合中
    /**
     * aspectInfoList 中是否有 该目标targetClass 织入切面对象,如果有放入粗筛集合中
     * @param targetClass 目标Class 对象
     * @param aspectInfoList 切面对象集合
     * @return 该目标类的粗筛结束后要织入的切面集合对象
     */
    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(Class<?> targetClass,List<AspectInfo> aspectInfoList) {
        List<AspectInfo> roughMatchdAspectList = new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfoList){
            PointcutLocator pointcutLocator = aspectInfo.getPointcutLocator();
            if (pointcutLocator.roughMatches(targetClass)){
                roughMatchdAspectList.add(aspectInfo);
            }
        }
        return roughMatchdAspectList;
    }

    // 2.拼装AspectInfoList
    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> aspectInfoList = new ArrayList<>();
        for (Class<?> aspectClass : aspectSet){
            if (verifyAspect(aspectClass)){
                Order orderAnnotation = aspectClass.getAnnotation(Order.class);
                Aspect aspectAnnotation = aspectClass.getAnnotation(Aspect.class);
                PointcutLocator pointcutLocator = new PointcutLocator(aspectAnnotation.pointcut());
                DefaultAspect aspect = (DefaultAspect)beanContainer.getBean(aspectClass);
                aspectInfoList.add(new AspectInfo(orderAnnotation.value(),aspect,pointcutLocator));
            }else{
                //不遵守规范则直接抛出异常
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfoList;
    }

    // 循环执行 aspect
//    private void weaveByCategory(Class<? extends Annotation> category, List<AspectInfo1> aspectInfo1List) {
//        // 1.获取被代理类的集合
//        Set<Class<?>> classSet = beanContainer.getClassesByAnnotation(category);
//        if (ValidationUtil.isEmpty(classSet)){return;}
//        // 2.遍历被代理类，分别为每个代理类生成动态代理实例
//        for (Class<?> tagetClass : classSet){
//            AspectListExecutor aspectListExecutor = new AspectListExecutor(tagetClass, aspectInfo1List);
//            Object proxyBean = ProxyCreator.createProxy(tagetClass, aspectListExecutor);
//            beanContainer.addBean(tagetClass,proxyBean);
//        }
//    }

    // 框架中一定要遵守给Aspect类添加@Aspect和@Order标签的规范，同时，必须遵守继承自DefaultAspect
    // 此外，@Aspect的属性值不能是它本身
    private boolean verifyAspect(Class<?> aspectClass) {
        return (aspectClass.isAnnotationPresent(Aspect.class) ||
                aspectClass.isAnnotationPresent(Order.class)) &&
                DefaultAspect.class.isAssignableFrom(aspectClass);
    }
}
